﻿namespace DatabaseDoc.Library
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization;

    using global::DatabaseDoc.Library.Factories;

    /// <summary>
    /// Database documentation
    /// </summary>
    public class DatabaseDoc
    {        
        /// <summary>
        /// Initializes a new instance of the <see cref="DatabaseDoc"/> class.
        /// </summary>
        /// <param name="connectorKey">The connector key.</param>
        public DatabaseDoc(string connectorKey)
        {
            var databaseConnectorFactory = new DatabaseConnectorFactory();
            this.DatabaseConnector = databaseConnectorFactory.CreateDatabaseConnector(connectorKey);

            if (this.DatabaseConnector == null)
            {
                throw new ConnectorNotFoundException(string.Format("Connector {0} not found", connectorKey));
            }
        }

        /// <summary>
        /// Gets or sets the servers.
        /// </summary>
        /// <value>The servers.</value>
        public IList<Server> Servers { get; set; }

        /// <summary>
        /// Gets or sets the database connector.
        /// </summary>
        /// <value>The database connector.</value>
        private IDatabaseConnector DatabaseConnector { get; set; }

        /// <summary>
        /// Generates the specified server names.
        /// </summary>
        /// <param name="serverNames">The server names.</param>
        /// <param name="databaseName">Name of the database.</param>
        /// <param name="tableName">Name of the table.</param>
        /// <returns>The database doc structure</returns>
        public DatabaseDoc Populate(string[] serverNames, string databaseName = null, string tableName = null)
        {
            var databaseDoc = this.DatabaseConnector.Populate(serverNames, databaseName, tableName);
            return databaseDoc;
        }

        public void Output(string fileName, string outputterKey)
        {
            var outputterFactory = new OutputterFactory();
            var outputter = outputterFactory.CreateOutputter(outputterKey);

            if (outputter == null)
            {
                throw new OutputterNotFoundException(string.Format("Outputter {0} not found", outputterKey));
            }

            outputter.Output(this, fileName);
        }

        public void GenerateScripts(string serverName, string databaseName, string tableName, string scriptPrefix)
        {
            this.DatabaseConnector.GenerateScripts(serverName, databaseName, tableName, scriptPrefix);
        }

        public void RunScripts(string serverName, string databaseName, string runScriptVersion)
        {            
            var path = Environment.CurrentDirectory;

            var directoryInfo = new DirectoryInfo(path);
            var sqlFiles = directoryInfo.GetFiles("*.sql").OrderBy(f => f.Name);

            var fileContents = new List<string>(sqlFiles.Count());
            foreach (var sqlFile in sqlFiles)
            {
                var fileContent = File.ReadAllText(sqlFile.FullName);
                fileContents.Add(fileContent);                
            }

            if (fileContents.Count() == 0)
            {
                return;
            }

            this.DatabaseConnector.RunScripts(serverName, databaseName, fileContents);
        }

        public bool DatabaseExists(string serverName, string databaseName)
        {
            return this.DatabaseConnector.DatabaseExists(serverName, databaseName);
        }

        public void CreateDatabase(string serverName, string databaseName)
        {
            this.DatabaseConnector.CreateDatabase(serverName, databaseName);
        }

        public void DropDatabase(string serverName, string databaseName)
        {
            this.DropDatabase(serverName, databaseName);
        }

        public void RunScripts(string serverName, string databaseName, IEnumerable<string> scripts)
        {
            this.DatabaseConnector.RunScripts(serverName, databaseName, scripts);
        }
    }

    [Serializable]
    public class ConnectorNotFoundException : Exception
    {
        public ConnectorNotFoundException()
        {
        }

        public ConnectorNotFoundException(string message)
            : base(message)
        {
        }

        public ConnectorNotFoundException(string message, Exception inner)
            : base(message, inner)
        {
        }

        protected ConnectorNotFoundException(
            SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }
    }

    [Serializable]
    public class OutputterNotFoundException : Exception
    {
        public OutputterNotFoundException()
        {
        }

        public OutputterNotFoundException(string message)
            : base(message)
        {
        }

        public OutputterNotFoundException(string message, Exception inner)
            : base(message, inner)
        {
        }

        protected OutputterNotFoundException(
            SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }
    }
}